#pragma once

#ifdef _MSC_VER
/* can't seem to define these at a non-global scope and have the desired effect
if you're on msvc 2010+, define the symbols in your cpp.user properties */
//#   define _CRT_NONSTDC_NO_DEPRECATE
//#   define _CRT_SECURE_NO_WARNINGS
#   pragma warning(disable: 4200)
#endif

#ifdef _MSC_VER
#   include <io.h>
#endif
#if defined(_MSC_VER) && _MSC_VER < 1600
#	include <boost/cstdint.hpp>
	using boost::uint32_t;
	using boost::int64_t;
#else
#   include <stdint.h>
#endif
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef unix
#   include <unistd.h>
#endif

/* C/C++ DIFFERENCES */

#ifndef __cplusplus
#	if __STDC_VERSION__ >= 199901L && !defined(_MSC_VER)
#		include <stdbool.h>
#	elif !defined(bool)
#		define bool int
#		define true 1
#		define false 0
#	endif
#endif

#if !defined(__cplusplus) && __STDC_VERSION < 199901L
#   define inline __inline
#endif

#if defined(__cplusplus) && defined(__GNUC__)
//#   pragma GCC diagnostic ignored "-fpermissive"
#endif

#ifdef __cplusplus
#	define LINKAGE(strlit) extern strlit
#else
#   define LINKAGE(strlit)
#endif

#ifdef __cplusplus
#   define C_LINKAGE_BEGIN extern "C" {
#   define C_LINKAGE_END }
#else
#   define C_LINKAGE_BEGIN
#   define C_LINKAGE_END
#endif

#ifdef __cplusplus
    #define STATIC_ASSERT static_assert
#else
    /* could use #assert? */
    #define STATIC_ASSERT4(cond, msg) \
        typedef char static_assertion_##msg[(!!(cond)) * 2 - 1]
    #define STATIC_ASSERT3(cond, line) \
        STATIC_ASSERT4(cond, static_assertion_at_line_##line)
    #define STATIC_ASSERT2(cond, line) STATIC_ASSERT3(cond, line)
    #define STATIC_ASSERT(cond, msg)    STATIC_ASSERT2(cond, __LINE__)
#endif

#define CHECK_TYPE_SIZE(type, size) \
    STATIC_ASSERT(sizeof(type) == size, #type " has wrong size")

#if defined(nullptr)
#   error
#endif
#ifdef __cplusplus
    // clang has __has_feature(cxx_nullptr) but it doesn't work yet
#   if      defined(__GNUC__) && __GNUC__ <= 4 && __GNUC_MINOR__ < 6 && !defined(__clang__)
        const                        // this is a const object...
        class {
        public:
          template<class T>          // convertible to any type
            operator T*() const      // of null non-member
            { return 0; }            // pointer...
          template<class C, class T> // or any type of null
            operator T C::*() const  // member pointer...
            { return 0; }
        private:
          void operator&() const;    /* whose address can't be taken */
        } nullptr = {};              // and whose name is nullptr
#   endif
#else
#   define nullptr NULL
#endif

#ifndef __cplusplus
#   define decltype typeof
#endif

#ifdef __cplusplus
#   define VOID_CAST(type, castee) static_cast<type>(castee)
#   define void_cast VOID_CAST
#else
#   define VOID_CAST(type, castee) (type)(castee)
#   define void_cast VOID_CAST
#endif

/* STANDARDS DIFFERENCES */

#define typeof __typeof

/* COMPILER DIFFERENCES */

#ifdef _MSC_VER
#   define DLL_EXPORT __declspec(dllexport)
#else
#   define DLL_EXPORT
#endif

#ifdef _MSC_VER
#   include <BaseTsd.h>
#   define ssize_t SSIZE_T
#endif

#if defined(__cplusplus) && !defined(_MSC_VER)
#   define override
#endif

#define TYPEATTR_PACKED __attribute__((packed))

#ifdef _MSC_VER
#	define FUNCATTR_FORMAT(archetype, strindex, firstchk)
#	define FUNCATTR_MUST_CHECK
#   define FUNCATTR_OVERRIDE override
#   define TYPEATTR_MS_STRUCT
#	define VARATTR_THREAD __declspec(thread)
#	define VARATTR_UNUSED
#else
#	define FUNCATTR_FORMAT(archetype, strindex, firstchk) \
		__attribute__((format(archetype, strindex, firstchk)))
#	define FUNCATTR_MUST_CHECK __attribute__((warn_unused_result))
#   define FUNCATTR_OVERRIDE
#   define TYPEATTR_MS_STRUCT __attribute__((ms_struct))
#	define VARATTR_THREAD __thread
#	define VARATTR_UNUSED __attribute__((unused))
#endif

int64_t lseek64(int, int64_t, int);
int fseek64(FILE *, int64_t, int);
int64_t ftell64(FILE *);
int64_t atoi64(char const *);

#ifdef _MSC_VER
#	define __func__ __FUNCTION__
#endif

/*
// don't use restrict, it clashes in c++. use __restrict
//#if __STDC_VERSION__ < 199901L
//#	define restrict __restrict
//#endif
*/

/* PLATFORM DIFFERENCES */

#if !defined(__CYGWIN__) && _POSIX_C_SOURCE < 199309L
    struct timespec {
        time_t   tv_sec;
        long     tv_nsec;
    };
#endif

#if !defined(_POSIX_TIMERS)
#   define COMPAT_TIMERS
	typedef enum {
		CLOCK_REALTIME
	} clockid_t;
	LINKAGE("C") int clock_gettime(clockid_t clk_id, struct timespec *tp);
#endif

#if _POSIX_VERSION < 200112L && _XOPEN_SOURCE < 600

#	define COMPAT_MEMALIGN
	LINKAGE("C") int posix_memalign(void **memptr, size_t alignment, size_t size);
    enum {
        POSIX_FADV_SEQUENTIAL,
        POSIX_FADV_NORMAL,
    };

#   define COMPAT_ADVANCED_REALTIME
    LINKAGE("C") int posix_fadvise(int fd, int64_t offset, int64_t len, int advice);

#endif

#if !defined(unix)
#   define COMPAT_UNIX
    typedef uint32_t uid_t;
    typedef uint32_t gid_t;
    LINKAGE("C") uid_t geteuid(void);
    LINKAGE("C") gid_t getegid(void);
#endif

#if _POSIX_C_SOURCE < 1 && !defined(_POSIX_SOURCE)
    LINKAGE("C") char *strtok_r(char *str, const char *delim, char **saveptr);
#endif
